
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include "CRC.h"
#include "config.h"

#ifndef ADD_BIN_LEN
#define ADD_BIN_LEN (0) /*是否在bin文件开头添加文件大小 1:添加  0:不添加*/
#endif
#ifndef TARGET_PLATFORM_LITTLE_ENDIAN
#define TARGET_PLATFORM_LITTLE_ENDIAN   (1) /*目标平台是否是小端模式 1:小端  0:大端*/
#endif

uint64_t get_file_size(FILE *stream);
void value2BigEndian(void *dest,const void *src,size_t count);
void value2LittleEndian(void *dest,const void *src,size_t count);

//  argv[1]:输入文件名  argv[2]:输出文件名 argv[3]:多少字节添加一个CRC 0xffffffff:整个文件添加一个crc
int main(int argc,char *argv[])
{
#ifdef DEBUG
    argc = 4;
    argv[1] = "../a.bin";
    argv[2] = "out.bin";
    argv[3] = "256";
    printf("version:%d.%d.%d Debug\n",BinAddCrc_VERSION_MAJOR,BinAddCrc_VERSION_MINOR,BinAddCrc_VERSION_PATCH);
#else
printf("version:%d.%d.%d Release\n",BinAddCrc_VERSION_MAJOR,BinAddCrc_VERSION_MINOR,BinAddCrc_VERSION_PATCH);
#endif

    if(argc != 4)
    {
        printf("请输入:输入文件名 输出文件名 CRC添加长度\n");
        return -1;
    }
    char* endptr;
    uint64_t read_len = strtoimax(argv[3],&endptr,0);

    FILE *fp_src = fopen(argv[1],"rb");
    if(fp_src == NULL)
    {
        printf("open %s failed:%s\n",argv[1],strerror(errno));
        return -1;  
    }

    FILE *fp_save = fopen(argv[2],"wb+"); 
    if(fp_save == NULL)
    {
        printf("create %s failed:%s\n",argv[2],strerror(errno));
        return -1;  
    }
    /*申请读缓冲*/
    uint8_t *read_buf = NULL;
    if(read_len == 0xffffffff)
        read_len = get_file_size(fp_src);
 
    read_buf = (uint8_t*)malloc(sizeof(char)*read_len+8);
    if(read_buf == NULL)
    {
        fclose(fp_src);
        fclose(fp_save);
        remove(argv[2]);
        printf("内存申请失败\n");
        return -1;
    }
    if(ADD_BIN_LEN)/*添加文件长度*/
    {
        uint32_t file_size = get_file_size(fp_src);
        if(file_size == (uint32_t)-1)
        {
            fclose(fp_src);
            fclose(fp_save);
            remove(argv[2]);
            printf("文件大小获取失败\n");
            return -1;
        }        
        typeof(file_size) FileSize = file_size;
        printf("%s file size %#010X\n",argv[1],file_size);
        if(TARGET_PLATFORM_LITTLE_ENDIAN)/*小端*/
            value2LittleEndian(&FileSize,&file_size,sizeof(FileSize));
        else/*大端*/
            value2BigEndian(&FileSize,&file_size,sizeof(FileSize));
        fwrite(&FileSize,sizeof(char),sizeof(FileSize),fp_save);
    }
    uint32_t read_return = 0;
    while((read_return = fread(read_buf,sizeof(char),read_len,fp_src)) > 0)
    {
        uint32_t crc_value;

        if(ferror(fp_src))
        {
            printf("read error\n");
            break;
        }
        
        // crc_value = CRC16Check(&crc_16_MODBUS,read_buf,read_return);
        uint8_t crc_len = crcCheck(&crc_16_MODBUS, read_buf,read_return,&crc_value,sizeof(crc_16_MODBUS));
        if(!TARGET_PLATFORM_LITTLE_ENDIAN)/*大端*/
            value2BigEndian(&read_buf[read_return],&crc_value,crc_len);
        else/*小端*/
            value2LittleEndian(&read_buf[read_return],&crc_value,crc_len);
        printf("addr:%#010lx ~ ",ftell(fp_save));
        fwrite(read_buf,sizeof(char),read_return+crc_len,fp_save);
        printf("%#010lx\t",ftell(fp_save)-1-crc_len);
        printf("CRC:%#06x \n",crc_value); 
    }

    free(read_buf);
    fclose(fp_src);
    fclose(fp_save);
    printf("add crc ok\n");
    return 0;
}

/**
 * @brief   获取文件大小
 * 
 * @param   stream      
 * @return  uint64_t -1：获取失败 其他:获取成功
 */
uint64_t get_file_size(FILE *stream)
{
	uint64_t file_size = -1;
	uint64_t cur_offset = ftell(stream);	// 获取当前偏移位置
	if (cur_offset == (uint64_t)-1) {
		printf("ftell failed :%s\n", strerror(errno));
		return -1;
	}
    if (fseek(stream, 0, SEEK_END) != 0) {	// 移动文件指针到文件末尾
		printf("fseek failed: %s\n", strerror(errno));
		return -1;
	}
	file_size = ftell(stream);	// 获取此时偏移值，即文件大小
	if (file_size == (uint64_t)-1) {
        printf("ftell failed :%s\n", strerror(errno));
	}
    if (fseek(stream, cur_offset, SEEK_SET) != 0) {	// 将文件指针恢复初始位置
		printf("fseek failed: %s\n", strerror(errno));
		return -1;
	}
	return file_size;
}

/**
 * @brief   将src所指向的数据转换为大端序
 * 
 * @param   dest     目标地址   
 * @param   src      源地址   
 * @param   count    被转换数据的大小(单位字节)   
 */
void value2BigEndian(void *dest,const void *src,size_t count)
{
    /*判断自身的存储方式*/
    int a = 0x01;
    if(*(char *)&a == 0x01)/*小端*/
    {
        for(size_t i = 0;i < count;i++)
        {
            *((char *)dest+count-1-i) = *((char *)src+i);
        }
        return;
    }
    memcpy(dest,src,count);
}

/**
 * @brief   将src所指向的数据转换为小端序
 * 
 * @param   dest     目标地址   
 * @param   src      源地址   
 * @param   count    被转换数据的大小(单位字节)   
 */
void value2LittleEndian(void *dest,const void *src,size_t count)
{
    /*判断自身的存储方式*/
    int a = 0x01;
    if(*(char *)&a == 0x01)/*小端*/
    {
        memcpy(dest,src,count);
        return;
    }
        for(size_t i = 0;i < count;i++)
    {
        *((char *)dest+count-1-i) = *((char *)src+i);
    }
}
